package org.example.service.base;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.CharsetUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.interactive.documentnavigation.outline.PDDocumentOutline;
import org.apache.pdfbox.pdmodel.interactive.documentnavigation.outline.PDOutlineItem;
import org.example.pojo.Bookmark;
import org.example.pojo.TitleAndPageNumber;
import org.example.utils.DateUtils;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Optional;
import java.util.function.Function;


@Slf4j
public abstract class AbstractBookmarkProcessTemplate {
    public void writeBookmark2File(String bookmarkTxtFile, String inputPDFFile, Integer offset) {
        int splitPoint = inputPDFFile.indexOf(".pdf");
        String inputFileName = inputPDFFile.substring(0, splitPoint);
        String ext = inputPDFFile.substring(splitPoint);
        String outputPDFFile = inputFileName + DateUtils.getDataStr() + ext;
        writeBookmark2File(bookmarkTxtFile, inputPDFFile, outputPDFFile, offset);
    }

    //String bookmarkTxtFile, String inputPDFFile, Integer offset, String outputPDFFile
    public void writeBookmark2File(String bookmarkTxtFile, String inputPDFFile, String outputPDFFile, Integer offset) {
        ArrayList<Bookmark> bookmarks = doGenerateBookmarkList(bookmarkTxtFile);
        //更多请阅读：https://www.yiibai.com/pdfbox/pdfbox_reading_text.html
        try (PDDocument document = PDDocument.load(new File(inputPDFFile))) {
            HashMap<Integer, PDOutlineItem> lastLevel2PDOutlineItem = new HashMap<>();

            // 创建文档大纲
            PDDocumentOutline outline = new PDDocumentOutline();
            document.getDocumentCatalog().setDocumentOutline(outline);

            for (Bookmark bookmark : bookmarks) {
                String title = bookmark.getTitle();
                // 框架索引从0开始，减1和框架校准
                Integer pageNumber = bookmark.getPageNumber()  + offset-2;
                Integer level = bookmark.getLevel();
                PDOutlineItem parent = lastLevel2PDOutlineItem.get(level - 1);
                // 缓存中没有上一级父书签
                if (parent == null) {
                    // 创建根书签即顶级书签指的是章
                    PDOutlineItem rootBookmark = new PDOutlineItem();
                    rootBookmark.setTitle(title);
                    // 章书签添加到大纲中
                    outline.addLast(rootBookmark);

                    // 设置根书签跳转到的页面
                    PDPage destPage = document.getPage(pageNumber); // 跳转到指定页
                    PDOutlineItem dest = new PDOutlineItem();
                    dest.setDestination(destPage);
                    rootBookmark.setDestination(destPage);
                    // 更新最近章
                    // 没有找到第几章和数字与点的小结，都会被设置为level=0 所以这里只是更新有章的，其他等级为0却不是章标题的不用作为父书签。
                    if (title.matches(".*第.*章.*")) {
                        lastLevel2PDOutlineItem.put(level, rootBookmark);
                    }
                } else {
                    // 创建子书签小节
                    PDOutlineItem childBookmark = new PDOutlineItem();
                    childBookmark.setTitle(title);
                    parent.addLast(childBookmark);

                    // 设置子书签跳转到的页面
                    PDPage destPage = document.getPage(pageNumber); // 跳转到指定页
                    PDOutlineItem dest = new PDOutlineItem();
                    dest.setDestination(destPage);
                    childBookmark.setDestination(destPage);
                    // 更新最近的章节
                    lastLevel2PDOutlineItem.put(bookmark.getLevel(), childBookmark);
                }
            }
            // 保存文档
            document.save(outputPDFFile);
            System.out.println("PDF with bookmarks created successfully.");
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    // 子类必须实现的方法，利用现有的fromTxtGenerateBookmarkList方法，定义自己逻辑传入即可
    abstract public ArrayList<Bookmark> doGenerateBookmarkList(String txtFile);

    /**
     * 定义生成bookmark的流程，具体实现看子类
     * @Param txtFile 书签的文本文件
     * @Param beforeConvert 原始字符串预处理，如遇到空白行则会返回Optional.empty() 所以可以不用删除空行
     * @Param convertTitleAndPageNumber 分割字符串为title和pageNumber 如果不能分割为标题和页码则会返回Optional.empty() 此行不会生成Bookmark
     * @Param convert2Bookmark 将title和pageNumber 按照等级规则 生成ArrayList<Bookmark>
     *
     */
    public ArrayList<Bookmark> fromTxtGenerateBookmarkList(String txtFile, Function<String, Optional<String>> beforeConvert, Function<Optional<String>, Optional<TitleAndPageNumber>> convertTitleAndPageNumber, Function<Optional<TitleAndPageNumber>, Optional<Bookmark>> convert2Bookmark) {
        ArrayList<Bookmark> bookmarks = new ArrayList<>();
        ArrayList<String> readUtf8Lines = FileUtil.readLines(txtFile, CharsetUtil.UTF_8, new ArrayList<>());
        Function<String, Optional<Bookmark>> bookmarkFunction = convert2Bookmark.compose(convertTitleAndPageNumber.compose(beforeConvert));
        for (String readUtf8Line : readUtf8Lines) {
            Optional<Bookmark> bookmark = bookmarkFunction.apply(readUtf8Line);
            if (bookmark.isPresent()) {
                bookmarks.add(bookmark.get());
            }
        }
        return bookmarks;
    }


}
